<?php

namespace AmazonAdvertisingApi;

use GuzzleHttp\Psr7\Request;
use InvalidArgumentException;
use RuntimeException;
use AmazonAdvertisingApi\Contract\RequestSignerContract;

class Configuration
{
    /**
     * @const array[string]
     */
    public const REQUIRED_CONFIG_KEYS = [
        "lwaClientId", "lwaClientSecret",  "endpoint"
    ];
    protected string $userAgent = 'vv_guo/amazon-advertising-api/1.0.0 (Language=PHP)';

    /**
     * @var RequestSignerContract
     */
    protected RequestSignerContract $requestSigner;
    /**
     * Auth object for the SP API
     *
     * @var Authentication
     */
    protected Authentication $auth;
    /**
     * @var string
     */
    protected string $endpoint=Endpoint::NA;

    protected string $scope;
    /**
     * Debug switch (default set to false)
     *
     * @var bool
     */
    protected $debug = false;

    /**
     * Debug file location (log to STDOUT by default)
     *
     * @var string
     */
    protected string $debugFile = 'php://output';
    /**
     * Debug file location (log to STDOUT by default)
     *
     * @var string
     */
    protected static $tempFolderPath = null;

    /**
     * Constructor
     * @param array $configurationOptions
     * @throws RuntimeException
     */
    public function __construct(array $configurationOptions)
    {
        // Make sure all required configuration options are present
        $missingKeys = [];
        foreach (static::REQUIRED_CONFIG_KEYS as $key) {
            if (!isset($configurationOptions[$key])) {
                $missingKeys[] = $key;
            }
        }
        if (count($missingKeys) > 0) {
            throw new RuntimeException("Required configuration values were missing: " . implode(", ", $missingKeys));
        }

        if (
            (isset($configurationOptions["accessToken"]) && !isset($configurationOptions["accessTokenExpiration"])) ||
            (!isset($configurationOptions["accessToken"]) && isset($configurationOptions["accessTokenExpiration"]))
        ) {
            throw new RuntimeException('If one of the `accessToken` or `accessTokenExpiration` configuration options is provided, the other must be provided as well');
        }

        $options = array_merge(
            $configurationOptions,
            [
                "accessToken" => $configurationOptions["accessToken"] ?? null,
                "accessTokenExpiration" => $configurationOptions["accessTokenExpiration"] ?? null,
                "onUpdateCredentials" => $configurationOptions["onUpdateCredentials"] ?? null,
                "scope"=>$configurationOptions["scope"]??null
            ]
        );

        $this->endpoint = $options["endpoint"];
        $this->auth = new Authentication($options);

        $this->setRequestSigner($options["requestSigner"] ?? $this->auth);
    }
    public function getRequestSigner(): RequestSignerContract
    {
        return $this->requestSigner;
    }

    public function setRequestSigner(RequestSignerContract $requestSigner): void
    {
        $this->requestSigner = $requestSigner;
    }

    /**
     * Gets the host
     *
     * @return string Host
     */
    public function getHost()
    {
        return $this->endpoint;
    }
    /**
     * Sets the user agent of the api client
     *
     * @param string $userAgent the user agent of the api client
     *
     * @throws InvalidArgumentException
     * @return $this
     */
    public function setUserAgent($userAgent)
    {
        if (!is_string($userAgent)) {
            throw new InvalidArgumentException("User-agent must be a string.");
        }

        $this->userAgent = $userAgent;
        return $this;
    }

    /**
     * Gets the user agent of the api client
     *
     * @return string user agent
     */
    public function getUserAgent(): string
    {
        return $this->userAgent;
    }

    /**
     * Sets debug flag
     *
     * @param bool $debug Debug flag
     *
     * @return $this
     */
    public function setDebug($debug)
    {
        $this->debug = $debug;
        return $this;
    }

    /**
     * Gets the debug flag
     *
     * @return bool
     */
    public function getDebug()
    {
        return $this->debug;
    }

    /**
     * Sets the debug file
     *
     * @param string $debugFile Debug file
     *
     * @return $this
     */
    public function setDebugFile($debugFile)
    {
        $this->debugFile = $debugFile;
        return $this;
    }

    /**
     * Gets the debug file
     *
     * @return string
     */
    public function getDebugFile()
    {
        return $this->debugFile;
    }

    /**
     * Sets the temp folder path
     *
     * @param ?string $tempFolderPath Temp folder path
     * @return void
     */
    public static function setTempFolderPath(?string $tempFolderPath = null): void
    {
        if ($tempFolderPath === null) {
            static::$tempFolderPath = sys_get_temp_dir();
        } else {
            static::$tempFolderPath = $tempFolderPath;
        }
    }

    /**
     * Gets the temp folder path
     *
     * @return string Temp folder path
     */
    public static function getTempFolderPath()
    {
        if (isset(static::$tempFolderPath) || static::$tempFolderPath === null) {
            static::setTempFolderPath();
        }
        return static::$tempFolderPath;
    }

    /**
     * Get LWA client ID.
     *
     * @return string
     */
    public function getLwaClientId(): ?string
    {
        return $this->auth->getLwaClientId();
    }

    /**
     * Set LWA client ID.
     *
     * @param string $lwaClientId
     * @return void
     */
    public function setLwaClientId(string $lwaClientId): void
    {
        $this->auth->setLwaClientId($lwaClientId);
    }

    /**
     * Get LWA client secret.
     *
     * @return string
     */
    public function getLwaClientSecret(): ?string
    {
        return $this->auth->getLwaClientSecret();
    }

    /**
     * Set LWA client secret.
     *
     * @param string $lwaClientSecret
     * @return void
     */
    public function setLwaClientSecret(string $lwaClientSecret): void
    {
        $this->auth->setLwaClientSecret($lwaClientSecret);
    }
    public function getScope():?string
    {
        return $this->auth->getScope();
    }
    public function setScope(string $scope):void
    {
        $this->auth->setScope($scope);
    }
    /**
     * Get LWA refresh token.
     *
     * @return string
     */
    public function getLwaRefreshToken(): ?string
    {
        return $this->auth->getLwaRefreshToken();
    }

    /**
     * Set LWA refresh token.
     *
     * @param string|null $lwaRefreshToken
     * @return void
     */
    public function setLwaRefreshToken(?string $lwaRefreshToken = null): void
    {
        $this->auth->setLwaRefreshToken($lwaRefreshToken);
    }


    /**
     * Get current SP API endpoint.
     *
     * @return string
     */
    public function getEndpoint(): string
    {
        return $this->endpoint;
    }

    /**
     * Set SP API endpoint. $endpoint should be one of the constants from Endpoint.php.
     *
     * @param string $endpoint
     * @return void
     */
    public function setEndpoint(string $endpoint): void
    {
        $this->endpoint = $endpoint;
        $this->auth->setEndpoint($endpoint);
    }

    /**
     * Sign a request to the amazon advertising api using the AWS Signature V4 protocol.
     *
     * @param Request $request The request to sign
     * @param string $scope The scope of the request, if it's grantless
     *
     * @return Request The signed request
     */
    public function signRequest($request): Request
    {
        return $this->requestSigner->signRequest($request);
    }
}
